/*
 * main.c
 *
 *  Created on: 2009-8-7
 *      Author: wyb
 */

#include "forecast.h"
#include "log.h"
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <pthread.h>

#define SIZE sizeof(struct sockaddr_in)

int xsockfd, xEngineSock;

char **appNames = 0;
char *LOCAL_IP_ADDRESS = "10.2.225.199";
int LOCAL_PORT = 2347;

/*
 * 创建socket连接
 */
void createSocket(int *sockfd, const char* ip, int port) {
	struct sockaddr_in server = { AF_INET, htons(port), { INADDR_ANY } };
	int optval = 1;

	server.sin_addr.s_addr = inet_addr(ip);
	if ((*sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		perror("socket call failed");
		exit(1);
	}
	if (setsockopt(*sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))
			< 0) {
		perror("socket reuse");
		close(*sockfd);
		exit(1);
	}
	if (bind(*sockfd, (struct sockaddr *) &server, SIZE) == -1) {
		//绑定
		perror("bind call error");
		close(*sockfd);
		exit(1);
	}
}

/*
 * 从字符串中获取获取名称为paraName的value
 * 参数：offset，字符串的偏移量
 *       result即名称为paraName的value，用于返回
 * 返回：处理完之后新的offset
 */
int getStringParameter(char* msg, int offset, const char* paraName,
		char* result) {
	int i, p;
	i = 0;
	while (paraName[i] != 0) {
		if (paraName[i] != msg[offset + i]) {
			return -1;
		}
		i++;
	}
	i++;
	p = 0;
	while (msg[offset + i] != '&' && msg[offset + i] != '\0' && msg[offset + i]
			!= '\r' && msg[offset + i] != '\n') {
		result[p] = msg[offset + i];
		i++;
		p++;
	}
	result[p] = '\0';
	return offset + i + 1;
}

/*
 * 从字符串中获取获取名称为paraName的value，并转成整形输出
 * 参数：offset，字符串的偏移量
 *       result即名称为paraName的value，用于返回
 * 返回：处理完之后新的offset
 */
int getIntParameter(char* msg, int offset, const char* paraName, int *result) {
	char temp[128];
	int ret;
	ret = getStringParameter(msg, offset, paraName, temp);
	*result = atoi(temp);
	return ret;
}

/*
 * 将字符串包装成Erlang可识别的格式
 * 参数：msg 欲包装的字符串
 *       bytes 包装完后的包
 * 返回：包装完后的数据长度
 */
int packetErlangData(const char* msg, char* bytes) {
	printf("SEND MESSAGE: %s \n", msg);
	int l = strlen(msg);
	bytes[0] = '\0';
	bytes[1] = '\0';
	bytes[2] = '\0';
	bytes[3] = (char) l;
	memcpy(&bytes[4], msg, l);
	return l + 4;
}

/*
 * 接受Erlang传来的数据，并将数据转成C可是别的格式
 * 参数：rev 用于返回收到的数据
 */
int receive(int sockfd, char * rev) {
	char temp[8];
	int p = 0;
	int ret;
	do {
		ret = recv(sockfd, &rev[p], 1, 0);
		if (ret == 0)
			return -1;
		if (rev[p] == '\0') {
			ret = recv(sockfd, temp, 3, 0);
			if (ret == 0)
				return -1;
		} else {
			p++;
		}
	} while (rev[p - 1] != '\n');
	rev[p] = '\0';

	printf("RECEIVE MESSAGE: %s \n", rev);
	return 0;
}

/*
 * 接收到msgtype=5的消息，处理该类型的消息
 */
void processMsgType5(char* msg, int offset) {
	int l, intLoad;
	float load;
	char appName[64];
	char str[256];
	char sev[256];
	offset = getStringParameter(msg, offset, "appname", appName);

	//TODO:预测
	xengine_mysql_init(URL, DB, USERNAME, PASSWORD);
	load = forecast(appName);
	xengine_mysql_close();

	intLoad = (int) (load * 100);
	sprintf(str, "msgtype=5&appname=%s&load=%d\r\n", appName, intLoad);

	l = packetErlangData(str, sev);
	send(xEngineSock, sev, l, 0);
}

/*
 * 收到了请求字符串，按照字符串的msgtype开始处理
 * 参数：msg 所收的请求字符串
 */
void processMessage(char* msg) {
	int offset = 0;
	int msgtype;
	offset = getIntParameter(msg, offset, "msgtype", &msgtype);
	switch (msgtype) {
	case 5:
		processMsgType5(msg, offset);
		break;
	}
}

/*
 * 开始监听端口
 */
void listenport(int *sockfd, int *newsockfd) {
	char rev[4096];
	int ret;
	ret = 0;
	while (1) {
		if (listen(*sockfd, 5) == -1) {
			perror("listen call error");
			close(xsockfd);
			exit(1);
		}
		printf("forecast-algo start listen....\n");
		if ((*newsockfd = accept(*sockfd, NULL, NULL)) == -1) {
			perror("accept call error");
			continue;
		} else {
			printf("connected...\n");
			do {
				ret = receive(*newsockfd, rev);
				if (ret == 0)
					processMessage(rev);
			} while (ret == 0);
			close(*newsockfd);
			printf("disconnected...\n");
		}
	}
}

/*
 * 主函数，创建连接并监听
 */
int main(int argc, char** argv) {
	set_project_name("xengine_forecast");

	createSocket(&xsockfd, LOCAL_IP_ADDRESS, LOCAL_PORT);
	listenport(&xsockfd, &xEngineSock);
	return (EXIT_SUCCESS);
}
